home *** CD-ROM | disk | FTP | other *** search
/ Mac Mania 5 / MacMania 5.toast / / Internet software / NewsWatcher / NW Source / Source / datetime.c < prev    next >
Text File  |  1997-01-09  |  7KB  |  346 lines

  1. /*----------------------------------------------------------------------------
  2.  
  3.     datetime.c
  4.  
  5.     This module converts RFC822 date/time lines into Mac-format date/time
  6.     strings in local time.
  7.     
  8.     Copyright © 1994-1997, Northwestern University.
  9.  
  10. ----------------------------------------------------------------------------*/
  11.  
  12. #include <stdlib.h>
  13. #include <string.h>
  14. #include <stdio.h>
  15. #include <ctype.h>
  16.  
  17. #include "glob.h"
  18. #include "datetime.h"
  19. #include "strutil.h"
  20.  
  21.  
  22.  
  23. /*----------------------------------------------------------------------------
  24.     Skip
  25.     
  26.     Skip white space and comments.
  27.     
  28.     Entry:    *str = string.
  29.     
  30.     Exit:    *str = pointer to first character following skipped white space
  31.                 and comments.
  32. ----------------------------------------------------------------------------*/
  33.  
  34. static void Skip (char **str)
  35. {
  36.     char *s;
  37.     short parenLevel;
  38.     
  39.     s = *str;
  40.     while (true) {
  41.         while (isLWSP(*s)) s++;
  42.         if (*s == '(') {
  43.             parenLevel = 1;
  44.             s++;
  45.             while (*s != 0 && parenLevel > 0) {
  46.                 if (*s == '(') {
  47.                     parenLevel++;
  48.                 } else if (*s == ')') {
  49.                     parenLevel--;
  50.                 }
  51.                 s++;
  52.             }
  53.         } else {
  54.             break;
  55.         }
  56.     }
  57.     *str = s;
  58. }
  59.  
  60.  
  61.  
  62. /*----------------------------------------------------------------------------
  63.     ParseNum
  64.     
  65.     Extract a long integer from a string.
  66.     
  67.     Entry:    *str = string.
  68.             min = minimum number of digits.
  69.             max = maximum number of digits.
  70.     
  71.     Exit:    function result = true if number parsed, false if syntax error.
  72.             *num = the number
  73.             *str = pointer to first character following parsed number.
  74. ----------------------------------------------------------------------------*/
  75.  
  76. static Boolean ParseNum (char **str, short min, short max, short *num)
  77. {
  78.     short n, numDigits;
  79.     char *p;
  80.     
  81.     p = *str;
  82.     if (!isdigit(*p)) return false;
  83.     n = CrackNum(&p);
  84.     numDigits = p - *str;
  85.     if (numDigits < min || numDigits > max) return false;
  86.     *str = p;
  87.     *num = n;
  88.     return true;
  89. }
  90.  
  91.  
  92.  
  93. /*----------------------------------------------------------------------------
  94.     FindDayOfWeek
  95.  
  96.     Given a string that might be a day of week abbreviation, return the number
  97.     of the day of the week (starting with Monday = 1, ...).
  98.     
  99.     Entry:    str = day of week string.
  100.     
  101.     Exit:    function result = day of week ordinal, or 0 if can't parse.
  102. ----------------------------------------------------------------------------*/
  103.  
  104. static short FindDayOfWeek (char *str)
  105. {
  106.     static char *days[7] = {
  107.         "MON", "TUE", "WED", "THU", "FRI", "SAT", "SUN"
  108.     };
  109.     short i;
  110.     
  111.     for (i = 0; i < 7; i++) {
  112.         if (MyStrNEqual(str, days[i], 3)) return i+1;
  113.     }
  114.     return 0;
  115. }
  116.  
  117.  
  118.  
  119. /*----------------------------------------------------------------------------
  120.     FindMonth
  121.  
  122.     Given a string that might be a month abbreviation, return the number
  123.     of the month (starting with January = 1, ...).
  124.     
  125.     Entry:    str = month string.
  126.     
  127.     Exit:    function result = month ordinal, or 0 if can't parse.
  128. ----------------------------------------------------------------------------*/
  129.  
  130. static short FindMonth (char *str)
  131. {
  132.     static char *months[12] = {
  133.         "JAN", "FEB", "MAR", "APR", "MAY", "JUN",
  134.         "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"
  135.     };
  136.     short i;
  137.     
  138.     for (i = 0; i < 12; i++) {
  139.         if (MyStrNEqual(str, months[i], 3)) return i+1;
  140.     }
  141.     return 0;
  142. }
  143.  
  144.  
  145.  
  146. /*----------------------------------------------------------------------------
  147.     Parse822Date
  148.  
  149.     Parse a date/time that's in RFC822/1123 header format:
  150.     
  151.     Entry:    date = RFC822/1123 date/time string.
  152.     
  153.     Exit:    function result = number of seconds since January 1, 1904, 
  154.                 the standard Mac date convention.
  155.             function result = 0 if the date could not be parsed.
  156.             
  157.     date-time = [ day "," ] date time
  158.     
  159.     day = "Mon" / "Tue" / "Wed" / "Thu" / "Fri" / "Sat" / "Sun"
  160.     
  161.     date = 1*2DIGIT month 2*4DIGIT
  162.     
  163.     month = "Jan" / "Feb" / "Mar" / "Apr" / "May" / "Jun" /
  164.             "Jul" / "Aug" / "Sep" / "Oct" / "Nov" / "Dec"
  165.             
  166.     time = hour zone
  167.     
  168.     hour = 2DIGIT ":" 2DIGIT [":" 2DIGIT]
  169.     
  170.     zone = "UT" / "GMT" / "EST" / "EDT" / "CST" / "CDT" /
  171.            "MST" / "MDT" / "PST" / "PDT" / ( ("+" / "-") 4DIGIT
  172. ----------------------------------------------------------------------------*/
  173.  
  174. unsigned long Parse822Date (char *date)
  175. {
  176.     DateTimeRec dt;
  177.     unsigned long result;
  178.     char *p;
  179.     short sign = 1;
  180.     short tzDelta = 0;
  181.  
  182.     memset(&dt, 0, sizeof(dt));
  183.  
  184.     p = date;
  185.     
  186.     /* Day of week and comma. */
  187.  
  188.     Skip(&p);
  189.     if (isalpha(*p)) {
  190.         if (FindDayOfWeek(p) == 0) return 0;
  191.         p+= 3;
  192.         Skip(&p);
  193.         if (*p != ',') return 0;
  194.         p++;
  195.     }
  196.     
  197.     /* Date. */
  198.  
  199.     Skip(&p);
  200.     if (!ParseNum(&p, 1, 2, &dt.day)) return 0;
  201.  
  202.     Skip(&p);
  203.     dt.month = FindMonth(p);
  204.     if (dt.month == 0) return 0;
  205.     p += 3;
  206.  
  207.     Skip(&p);
  208.     if (!ParseNum(&p, 2, 4, &dt.year)) return 0;
  209.     if (dt.year < 21) {
  210.         dt.year += 2000;
  211.     } else if (dt.year < 100) {
  212.         dt.year += 1900;
  213.     }
  214.     
  215.     /* Time. */
  216.  
  217.     Skip(&p);
  218.     if (!ParseNum(&p, 1, 2, &dt.hour)) return 0;
  219.     
  220.     Skip(&p);
  221.     if (*p != ':') return 0;
  222.     p++;
  223.     Skip(&p);
  224.     if (!ParseNum(&p, 2, 2, &dt.minute)) return 0;
  225.     
  226.     Skip(&p);
  227.     if (*p == ':') {
  228.         p++;
  229.         Skip(&p);
  230.         if (!ParseNum(&p, 2, 2, &dt.second)) return 0;
  231.     }
  232.     
  233.     /* Zone. */
  234.     
  235.     Skip(&p);
  236.     if (isalpha(*p)) {
  237.         if (MyStrNEqual(p, "GMT", 3)) {
  238.             tzDelta = 0;
  239.             p += 3;
  240.         } else if (MyStrNEqual(p, "UT", 2)) {
  241.             tzDelta = 0;
  242.             p += 2;
  243.         } else if (MyStrNEqual(p, "EST", 3)) {
  244.             sign = -1;
  245.             tzDelta = 500;
  246.             p += 3;
  247.         } else if (MyStrNEqual(p, "EDT", 3)) {
  248.             sign = -1;
  249.             tzDelta = 400;
  250.             p += 3;
  251.         } else if (MyStrNEqual(p, "CST", 3)) {
  252.             sign = -1;
  253.             tzDelta = 600;
  254.             p += 3;
  255.         } else if (MyStrNEqual(p, "CDT", 3)) {
  256.             sign = -1;
  257.             tzDelta = 500;
  258.             p += 3;
  259.         } else if (MyStrNEqual(p, "MST", 3)) {
  260.             sign = -1;
  261.             tzDelta = 700;
  262.             p += 3;
  263.         } else if (MyStrNEqual(p, "MDT", 3)) {
  264.             sign = -1;
  265.             tzDelta = 600;
  266.             p += 3;
  267.         } else if (MyStrNEqual(p, "PST", 3)) {
  268.             sign = -1;
  269.             tzDelta = 800;
  270.             p += 3;
  271.         } else if (MyStrNEqual(p, "PDT", 3)) {
  272.             sign = -1;
  273.             tzDelta = 700;
  274.             p += 3;
  275.         } else {
  276.             return 0;
  277.         }
  278.     } else if (*p == '+' || *p == '-') {
  279.         sign = *p == '+' ? +1 : -1;
  280.         p++;
  281.         Skip(&p);
  282.         if (!ParseNum(&p, 4, 4, &tzDelta)) return 0;
  283.     } else {
  284.         return 0;
  285.     }
  286.     
  287.     Skip(&p);
  288.     if (*p != 0) return 0;
  289.  
  290.     /* Convert to seconds since 1/1/1904 */
  291.     
  292.     DateToSeconds(&dt, &result);
  293.     result -= sign * (3600*((long)tzDelta/100) + 60*((long)tzDelta%100));
  294.     return result;
  295. }
  296.  
  297.  
  298. /*----------------------------------------------------------------------------
  299.     Cleanup822Date
  300.     
  301.     Converts an RFC822 date/time string into a Mac-style date/time string
  302.     in local time.
  303.     
  304.     Entry:    date = RFC822 date/time string.
  305.             
  306.     Exit:    date = Mac-style date/time string.
  307.     
  308.     If the string cannot be parsed or converted, or if the the use has
  309.     not set the local time zone, the string is unchanged.
  310. ----------------------------------------------------------------------------*/
  311.  
  312. void Cleanup822Date (char *date)
  313. {
  314.     MachineLocation    loc;
  315.     long gmtOffset;
  316.     unsigned long secs;
  317.     Str255 time;
  318.  
  319.     /* Get our location. Return if Map control panel not installed. */
  320.     
  321.     ReadLocation(&loc);
  322.     if (loc.latitude == 0 && loc.longitude == 0 && loc.u.gmtDelta == 0) return;
  323.  
  324.     /* Get our offset in seconds from GMT. */
  325.     
  326.     gmtOffset = loc.u.gmtDelta & 0x00FFFFFF;
  327.     if ((gmtOffset & 0x00800000) != 0) gmtOffset |= 0xFF000000;
  328.  
  329.     /* Try to parse the date we were passed. */
  330.     
  331.     secs = Parse822Date(date);
  332.     if (secs == 0) return;
  333.  
  334.     /* Correct for our timezone. */
  335.     
  336.     secs += gmtOffset;
  337.  
  338.     /* Convert it back into a Mac-style date/time string. */
  339.     
  340.     IUDateString(secs, abbrevDate, (StringPtr)date);
  341.     p2cstr((StringPtr)date);
  342.     strcat(date, " ");
  343.     IUTimeString(secs, true, time);
  344.     strcat(date, p2cstr(time));
  345. }
  346.